package com.ztesoft.book.api.aspect;

import com.alibaba.fastjson.JSON;
import com.ztesoft.book.api.annotation.SysLog;
import com.ztesoft.book.common.constant.Constant;
import com.ztesoft.book.common.utils.http.HttpContextUtils;
import com.ztesoft.book.common.utils.http.IpUtils;
import com.ztesoft.book.common.utils.jwt.JwtUtil;
import com.ztesoft.book.core.sys.syslog.dto.SysLogDTO;
import com.ztesoft.book.core.sys.syslog.service.SysLogService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Description: 系统日志，切面处理类
 * @Author: YangFan
 * @Date: 2021-03-29 10:48
 */
@Aspect
@Component
@Slf4j
public class SysLogAspect {

    private static final String AUTHORIZATION_HEADER = "token";

    private SysLogService sysLogService;

    @Autowired
    public SysLogAspect(SysLogService sysLogService) {
        this.sysLogService = sysLogService;
    }

    @Pointcut("@annotation(com.ztesoft.book.api.annotation.SysLog)")
    public void logPointCut() {
    }

    @SneakyThrows
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) {
        long beginTime = System.currentTimeMillis();
        // 执行方法
        Object result = point.proceed();
        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        // 保存日志
        saveSysLog(point, time);
        return result;
    }

    private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SysLogDTO sysLogDto = new SysLogDTO();
        SysLog sysLogAnnotation = method.getAnnotation(SysLog.class);
        if (sysLogAnnotation != null) {
            // 注解上的描述
            sysLogDto.setOperation(sysLogAnnotation.value());
        }
        // 请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        sysLogDto.setMethod(className + "." + methodName + "()");
        // 请求的参数
        Object[] args = joinPoint.getArgs();
        if (Objects.nonNull(args)) {
            List<Object> argsList = Arrays.asList(args);
            // 将 HttpServletResponse 和 HttpServletRequest 参数移除，不然会报异常
            List<Object> collect = argsList.stream().filter(o -> !(o instanceof HttpServletResponse || o instanceof HttpServletRequest)).collect(Collectors.toList());
            collect.toArray(args);
        }
        try {
            String params = JSON.toJSONString(args);
            sysLogDto.setParams(params);
        }
        catch (Exception e) {
            log.error("saveSysLog", e);
        }
        // 获取request
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        // 设置IP地址
        sysLogDto.setIp(IpUtils.getIpAddr(request));
        // 用户名
        String token = request.getHeader(AUTHORIZATION_HEADER);
        // 如果token是test，那么从Attribute获取自动生成的token
        if (Constant.TOKEN_TEST.equals(token)) {
            if (request.getAttribute("token") != null) {
                token = request.getAttribute("token").toString();
            }
            else {
                // 有test测试用token，但是未被shiro拦截（配了白名单），这里需要清除掉，否则下面解析报错
                token = null;
            }
        }
        String username = "";
        if (StringUtils.isNotBlank(token)) {
            username = JwtUtil.getClaim(token, Constant.USERNAME);
        }
        sysLogDto.setUsername(username);
        sysLogDto.setTime(time);
        // 保存系统日志
        sysLogService.save(sysLogDto);
    }
}
